// ################################
// ##
// ## File: main.cpp
// ##
// ################################



// ## Includes
// #######################################

#include <allegro5\allegro.h>
#include <allegro5\allegro_image.h>
#include <allegro5\allegro_font.h>
#include <allegro5\allegro_ttf.h>
#include <iostream>

#include "Globals.h"
#include "Geometry.h"

#include "Entity.h"
#include "EntityLiving.h"
#include "GameWorld.h"
#include "SelectionRectangle.h"


// ## Variables
// #######################################

// #######################
// ## ALLEGRO Variables
// #######################
ALLEGRO_DISPLAY *display = NULL;
ALLEGRO_EVENT_QUEUE *event_queue = NULL;
ALLEGRO_TIMER *timer = NULL;
ALLEGRO_MOUSE_CURSOR *cursor = NULL;
ALLEGRO_FONT *debugFont = NULL;

// #######################
// ## Gameplay Variables
// #######################
GameWorld *m_gameworld = NULL;
Viewport *m_viewport = NULL;
SelectionRectangle *selRect = NULL;

// #######################
// ## System Variables
// #######################
int gameState;

// GameLoop & Render
bool done = false;
bool redraw = true;

// #######################
// ## Input Variables
// #######################
bool mouseBtn[5 + 1];
bool KEYS[255];
Vector2 mousePos;
bool viewportPanned;



// ## Prototypes
// #######################################
void handleEvents(ALLEGRO_EVENT &ev);
void changeGamestate(int &state, int newState);
void update(void);
void draw(void);
void cleanup(void);



// ## Code
// #######################################
int main(void)
{
	// Local Variables
	ALLEGRO_EVENT ev;


	// ## Initialization
	std::cout << "Init...\n";
	// Init allegro
	if (!al_init())
		return -1;

	// Init display
	display = al_create_display(screen_w, screen_h);
	if (!display)
		return -1;

	// Install modules
	al_install_keyboard();
	al_install_mouse();

	// Init addons
	al_init_image_addon();
	al_init_font_addon();
	al_init_ttf_addon();
	al_init_primitives_addon();

	// Create timer
	timer = al_create_timer(1.0 / FPS);

	m_viewport = new Viewport(screen_w, screen_h);
	selRect = new SelectionRectangle();
	mousePos.x = 0.0f;
	mousePos.y = 0.0f;
	viewportPanned = false;
	gameState = -1;

	// Init and register events
	event_queue = al_create_event_queue();
	al_register_event_source(event_queue, al_get_display_event_source(display));
	al_register_event_source(event_queue, al_get_keyboard_event_source());
	al_register_event_source(event_queue, al_get_mouse_event_source());
	al_register_event_source(event_queue, al_get_timer_event_source(timer));

	m_gameworld = new GameWorld(screen_w, screen_h);
	std::cout << "Init done\n";


	// ## Loading
	std::cout << "Loading...\n";
	// Load cursor
	cursor = al_create_mouse_cursor(al_load_bitmap("res/cursor.png"), 8, 2);
	al_set_mouse_cursor(display, cursor);

	// Load font
	debugFont = al_load_font("res/consola.ttf", 16, 0);
	std::cout << "Loading done\n";


	// Change state to menu
	changeGamestate(gameState, TITLE);


	std::cout << "Starting main loop...\n";
	al_start_timer(timer);
	while (!done)
	{
		al_wait_for_event(event_queue, &ev);
		handleEvents(ev);

		if (ev.type == ALLEGRO_EVENT_TIMER)
		{
			update();
			redraw = true;
		}

		if (redraw && al_event_queue_is_empty(event_queue))
		{
			redraw = false;
			draw();
		}
		
	}
	std::cout << "Quitted main loop\n";

	std::cout << "Starting cleanup...\n";
	cleanup();
	std::cout << "Cleanup done\n";

	return 0;
}


// #######################
// ## Eventhandler
// #######################
void handleEvents(ALLEGRO_EVENT &ev)
{
	switch(ev.type)
	{
		// Mouse events
		case ALLEGRO_EVENT_MOUSE_AXES:
			mousePos.x = ev.mouse.x;
			mousePos.y = ev.mouse.y;
			if (mouseBtn[LMB])
			{
				// Selection rectangle
				selRect->setEndpoint(ev.mouse.x, ev.mouse.y);		
			}
			else if (mouseBtn[RMB])
			{
				// Viewport panning
				viewportPanned = true;
				Vector2 deltaPos;
				deltaPos.x = ev.mouse.dx;
				deltaPos.y = ev.mouse.dy;
				m_viewport->pan(deltaPos);
				/*
				m_viewport->setX(m_viewport->getX() - ev.mouse.dx);
				m_viewport->setY(m_viewport->getY() - ev.mouse.dy);
				*/
			}
			m_viewport->changeZoom(ev.mouse.dz);
			break;

		case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
			mouseBtn[ev.mouse.button] = true;
			if (mouseBtn[LMB])
			{
				selRect->setStartpoint(ev.mouse.x, ev.mouse.y);
				selRect->setEndpoint(ev.mouse.x, ev.mouse.y);
			}
			break;

		case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
			mouseBtn[ev.mouse.button] = false;
			switch(ev.mouse.button)
			{
				case LMB:
					// TODO: Add selection of entities
					// TODO: Add issueing commands to entities
					break;
				case RMB:
					// TODO: Context Menu
					if (!viewportPanned)
						printf("ContextMenu open\n");

					viewportPanned = false;
					
					break;
			}
			break;

		// Keyboard events
		case ALLEGRO_EVENT_KEY_DOWN:
			KEYS[ev.keyboard.keycode] = true;
			break;

		case ALLEGRO_EVENT_KEY_UP:
			KEYS[ev.keyboard.keycode] = false;
			switch (ev.keyboard.keycode)
			{
				case ALLEGRO_KEY_ESCAPE:
					done = true;
					break;
				case ALLEGRO_KEY_F1:
					//m_gameworld->addRandomHumanDummy(rand() % screen_w * 1.0f,rand() % screen_h * 1.0f);

					m_gameworld->addRandomHumanDummy(m_viewport->screenToGameworld(mousePos.x, mousePos.y));
					
					break;
			}
			break;

		// Form events
		case ALLEGRO_EVENT_DISPLAY_CLOSE:
			done = true;
			break;
	}
}

// #######################
// ## Change gamestate
// #######################
void changeGamestate(int &state, int newState)
{
	if (state == TITLE)
	{
		std::cout << "Leaving the TITLE state\n";
	}
	else if (state == PLAYING)
	{
		std::cout << "Leaving the PLAYING state\n";
	}
	else if (state == PAUSE)
	{
		std::cout << "Leaving the PAUSE state\n";
	}

	state = newState;

	if (state == TITLE)
	{
		std::cout << "Entering the TITLE state\n";
	}
	else if (state == PLAYING)
	{
		std::cout << "Entering the PLAYING state\n";
	}
	else if (state == PAUSE)
	{
		std::cout << "Entering the PAUSE state\n";
	}
}

// #######################
// ## Update Gameworld
// #######################
void update(void)
{
	
}

// #######################
// ## Render
// #######################
void draw(void)
{
	al_clear_to_color(al_map_rgb(255,255,255));

	//al_draw_textf(debugFont, al_map_rgb(0,255,0), 1, 0, 0, "MouseDown: %i %i %i %i %i", mouseBtn[1], mouseBtn[2], mouseBtn[3], mouseBtn[4], mouseBtn[5]);
	al_draw_textf(debugFont, al_map_rgb(0,255,0), 1, 0, 0, "Viewport: %f x %f (zoom: %f)", m_viewport->getX(), m_viewport->getY(), m_viewport->getZoom());

	
	if (mouseBtn[LMB])
		al_draw_rectangle(selRect->getX1(), selRect->getY1(), selRect->getX2(), selRect->getY2(), al_map_rgb(255,0,0), 1.0f);
		
	//TODO: add GUI (HUD)

	m_gameworld->draw(m_viewport);

	al_flip_display();
}

// #######################
// ## Cleanup
// #######################
void cleanup(void)
{
	if (display != NULL)
		al_destroy_display(display);
	if (event_queue != NULL)
		al_destroy_event_queue(event_queue);
	if (cursor != NULL)
		al_destroy_mouse_cursor(cursor);
	if (timer != NULL)
		al_destroy_timer(timer);

	if (m_gameworld != NULL)
		delete m_gameworld;
}